/**
 * WebSocket事件信息接口
 */
export interface UpcWebsocketEventInfo {
	isReconnect: boolean; // 是否是重连
	reconnectable: boolean; // 是否自动重连
	reconnectDivide: number; // 重连间隔 S
	reconnectMaxTimes?: number; // 最大重连次数
	reconnectTimes?: number; // 当前重连次数
	msg?: string; // 附加消息
	relogin_divide?: number; // 重连间隔（兼容旧版）
}

/**
 * WebSocket事件接口
 */
export interface UpcWebsocketEvent {
	type: UpcWebsocketEventType;
	data: UpcWebsocketEventInfo;
}

/**
 * WebSocket事件类型枚举
 */
export enum UpcWebsocketEventType {
	Connected,
	Disconnect,
	Reconnect
}

/**
 * WebSocket客户端类，封装了自动重连、心跳检测和主题订阅功能
 */
/**
 * WebSocket客户端类，用于管理WebSocket连接、消息订阅、心跳检测等功能
 */
export class WebsocketClient {
	// 存储WebSocket客户端实例
	private _client: any = null;
	// WebSocket URL
	private _url: string = '';
	// 连接状态
	public isConnect: boolean = false;
	// 重连次数
	public reConnectNum: number = 0;
	// 最大重连次数
	public reConnectMaxNum: number = 3;

	// 登录相关
	private _logined: boolean = false;
	private _loginTimer: any = null;
	private _loginResolve: ((value: boolean) => void) | null = null;

	// 心跳相关
	private _heartTimer: any = null;
	private _heartBeatResolve: ((value: any) => void) | null = null;
	private _heartBeatResolveTimer: any = null;

	// 重连句柄
	private _autoReloginTimer: any = null;
	private _autoReloginDivide: number = 10; // 每隔 10 S 发起一次重连

	// 实例事件, 需全局订阅
	private _instanceEventCallback: ((event: UpcWebsocketEvent) => void) | null = null;

	// 主题订阅
	private _subIndex: number = 0;
	private _subMap: Map<string, Map<number, (data: any) => void>> = new Map();

	/**
	 * 构造函数
	 * @param urlGenerator 生成WebSocket URL的函数
	 */
	constructor(url: string) {
		if (url) {
			throw new Error('url is not undefined');
		}
		this._url = url;
	}

	/**
	 * 注册实例事件回调
	 * @param fn 事件回调函数
	 */
	public onInstanceEvent(fn: (event: UpcWebsocketEvent) => void): void {
		if (typeof fn == 'function') {
			this._instanceEventCallback = fn;
		}
	}

	/**
	 * 触发实例事件
	 * @param type 事件类型
	 * @param data 事件数据
	 */
	private emitInstanceEvent(type: UpcWebsocketEventType, data: UpcWebsocketEventInfo): void {
		if (typeof this._instanceEventCallback == 'function') {
			this._instanceEventCallback({ type, data });
		}
	}

	/**
	 * 创建WebSocket连接
	 * @returns 连接结果
	 */
	private async createWebsocket(): Promise<boolean | { code: number; msg: string }> {
		// 清理现有连接
		if (this._client != null) {
			this._client.dispose();
			this._client = null;
		}

		// 创建WebSocket连接
		return new Promise((resolve: (connected: boolean) => void) => {
			this._loginResolve = resolve;
			this._client = new WebSocket(this._url);
		});
	}

	/**
	 * 开始连接
	 */
	public async start(): Promise<void> {
		const connected = await this.createWebsocket();

		// 判断ws对象是否创建成功，否就重连创建，是就开启心跳检测机制
		if (!connected) {
			this.autoRelogin();
		} else {
			this.keepHeartBeat(); // 开始心跳
		}
	}

	/**
	 * 暂停连接，清理相关资源
	 */
	public pause(): void {
		// 清理 socket
		if (this._client) {
			this._client.dispose();
			this._client = null;
		}

		// 如果有心跳，取消心跳
		if (this._heartTimer) {
			clearTimeout(this._heartTimer);
			this._heartTimer = null;
		}

		// 如果心跳正在进行, 取消回调
		if (this._heartBeatResolveTimer) {
			clearTimeout(this._heartBeatResolveTimer);
			this._heartBeatResolveTimer = null;
		}
		this._heartBeatResolve = null;

		this._loginResolve = null;

		// 如果在重连, 去掉重连
		if (this._autoReloginTimer) {
			clearTimeout(this._autoReloginTimer);
			this._autoReloginTimer = null;
		}

		// 清理主题订阅
		this._subIndex = 0;
		this._subMap.clear();

		this.isConnect = false; //连接状态
		this.reConnectNum = 0;
	}

	/**
	 * 自动重连
	 */
	private autoRelogin(): void {
		this._autoReloginTimer = setTimeout(async () => {
			this._autoReloginTimer = null;
			const ret = await this.createWebsocket();
			if (!ret) {
				return this.autoRelogin();
			} else {
				this.keepHeartBeat(); // 开始心跳
			}
			this.isConnect = true;

			// 重连成功
			this.emitInstanceEvent(UpcWebsocketEventType.Reconnect, {
				isReconnect: true,
				reconnectable: true,
				reconnectDivide: this._autoReloginDivide,
				reconnectMaxTimes: this.reConnectMaxNum,
				reconnectTimes: this.reConnectNum,
				msg: '',
				relogin_divide: this._autoReloginDivide
			});
		}, this._autoReloginDivide * 1000);
	}

	/**
	 * 连接建立成功回调
	 */
	public onConnect(): void {
		console.log(`webSocket已连接`);
		this.isConnect = true;
		this.reConnectNum = 0;

		if (typeof this._loginResolve == 'function') {
			this._loginResolve(true);
		}
		this._loginResolve = null;

		this.recoverSubscribe(); // 恢复订阅
	}

	/**
	 * 连接断开回调
	 * @param e 关闭事件
	 */
	public onDisconnect(e?: any): void {
		console.log(`webSocket已经关闭 ${e} `);

		// 清理 socket
		if (this._client) {
			this._client.dispose();
			this._client = null;
		}

		// 如果有心跳，取消心跳
		if (this._heartTimer) {
			clearTimeout(this._heartTimer);
			this._heartTimer = null;
		}

		// 如果心跳正在进行, 取消回调
		if (this._heartBeatResolveTimer) {
			clearTimeout(this._heartBeatResolveTimer);
			this._heartBeatResolveTimer = null;
		}
		this._heartBeatResolve = null;

		// 触发掉线通知
		this.emitInstanceEvent(UpcWebsocketEventType.Disconnect, {
			isReconnect: true,
			reconnectable: true,
			reconnectDivide: this._autoReloginDivide,
			reconnectMaxTimes: this.reConnectMaxNum,
			reconnectTimes: this.reConnectNum,
			msg: '',
			relogin_divide: this._autoReloginDivide
		});

		//被动断开，重新连接
		if (e && e?.code) {
			this.autoRelogin();
			console.log('websocket连接不上，请重新登录或联系开发人员!');
		}
	}

	/**
	 * 收到消息回调
	 * @param message 消息内容
	 */
	public onMessage(message: string): void {
		const msg = JSON.parse(message);

		if (!msg.channel) {
			console.error('onMessage error ');
			return;
		}

		if (!(typeof msg.text == 'string' && JSON.parse(msg.text).action_ans == 'subscriber successed!')) {
			this.handlePublish(msg);
		}
	}

	/**
	 * 保持心跳
	 */
	private keepHeartBeat(): void {
		this._heartTimer = setTimeout(async () => {
			this._heartTimer = null;
			const ret = await this.sendHeartBeat();
			if (ret?.code == 1) {
				return this.onDisconnect();
			}
			return this.keepHeartBeat();
		}, 30 * 1000); // 30秒发送一次心跳
	}

	/**
	 * 发送心跳
	 * @returns 心跳响应Promise
	 */
	private sendHeartBeat(): Promise<any> {
		return new Promise((resolve) => {
			this._heartBeatResolve = resolve;

			this._heartBeatResolveTimer = setTimeout(() => {
				this._heartBeatResolveTimer = null;
				if (this._heartBeatResolve !== null) {
					this._heartBeatResolve({ code: 100, ret_msg: 'sendHeartBeat timeout', data: null });
					this._heartBeatResolve = null;
				}
			}, 10 * 1000);

			const reqData = {
				action: 'req',
				channel: 'ping'
			};

			if (this._client) {
				this._client.sendMessage(JSON.stringify(reqData));
			}
		});
	}

	/**
	 * 处理发布的消息
	 * @param msg 消息对象
	 */
	private handlePublish(msg: any): void {
		const topic = msg.channel;
		const handleMap = this._subMap.get(topic);

		if (handleMap != null) {
			handleMap.forEach((fn) => {
				try {
					fn(JSON.parse(msg.text));
				} catch (e) {
					console.log(e);
				}
			});
		}
	}

	/**
	 * 恢复所有订阅
	 */
	private recoverSubscribe(): void {
		this._subMap.forEach((handleMap, topic) => {
			if (handleMap != null && handleMap.size > 0) {
				const reqData = {
					action: 'sub',
					channel: topic,
					data: {}
				};

				if (this._client) {
					this._client.sendMessage(JSON.stringify(reqData));
				}
			}
		});
	}

	/**
	 * 订阅主题
	 * @param topic 主题名称
	 * @param listener 监听器函数
	 * @returns 订阅句柄
	 */
	public subscribe(topic: string, listener: (data: any) => void): number | undefined {
		if (typeof listener != 'function') return;

		let handleMap = this._subMap.get(topic);
		if (handleMap == null) {
			handleMap = new Map();
			this._subMap.set(topic, handleMap);
		}

		const handle = ++this._subIndex;
		handleMap.set(handle, listener);

		if (handleMap.size == 1) {
			// 初始添加订阅, 需要向后台发送消息
			const reqData = {
				action: 'sub',
				channel: topic,
				data: {}
			};

			if (this._client) {
				this._client.sendMessage(JSON.stringify(reqData));
			}
		}

		return handle;
	}

	/**
	 * 取消订阅
	 * @param topic 主题名称
	 * @param handle 订阅句柄（可选）
	 */
	public unsubscribe(topic: string, handle?: number): void {
		const handleMap = this._subMap.get(topic);

		if (handleMap == null || handleMap.size == 0) {
			// 未订阅，无需退订
			return;
		}

		if (typeof handle == 'undefined') {
			// 全部退订
			handleMap.clear();
		} else {
			handleMap.delete(handle);
		}

		if (handleMap.size == 0) {
			// 向后台取消订阅
			const reqData = {
				action: 'unsub',
				channel: topic,
				data: {}
			};

			if (this._client) {
				this._client.sendMessage(JSON.stringify(reqData));
			}
		}
	}

	/**
	 * 清理资源
	 */
	public dispose(): void {
		// 清理 socket
		if (this._client) {
			this._client.dispose();
			this._client = null;
		}

		// 如果有心跳，取消心跳
		if (this._heartTimer) {
			clearTimeout(this._heartTimer);
			this._heartTimer = null;
		}

		// 如果心跳正在进行, 取消回调
		if (this._heartBeatResolveTimer) {
			clearTimeout(this._heartBeatResolveTimer);
			this._heartBeatResolveTimer = null;
		}
		this._heartBeatResolve = null;

		// 如果在重连, 去掉重连
		if (this._autoReloginTimer) {
			clearTimeout(this._autoReloginTimer);
			this._autoReloginTimer = null;
		}

		// 清理系统订阅
		this._instanceEventCallback = null;

		// 清理主题订阅
		this._subIndex = 0;
		this._subMap.clear();
	}
}

export default WebsocketClient;

/**
 * 例子
import WebsocketClient, { UpcWebsocketEventType } from './upc-websocket-client';

// 创建客户端实例
const client = new UpcWebsocketClient(urlGenerator);

// 注册事件回调
client.onInstanceEvent((event) => {
  switch (event.type) {
    case UpcWebsocketEventType.Connected:
      console.log('连接已建立');
      break;
    case UpcWebsocketEventType.Disconnect:
      console.log('连接已断开');
      break;
    case UpcWebsocketEventType.Reconnect:
      console.log('正在重连', event.data.reconnectTimes);
      break;
  }
});

// 开始连接
client.start();

// 订阅主题
const handle = client.subscribe('topic-name', (data) => {
  console.log('收到消息:', data);
});

// 取消订阅
// client.unsubscribe('topic-name', handle);

// 断开连接
// client.pause();

// 释放资源
// client.dispose();
 */
